home *** CD-ROM | disk | FTP | other *** search
/ Almathera Ten Pack 3: CDPD 3 / Almathera Ten on Ten - Disc 3: CDPD3.iso / fish / 001-100 / 001-025 / 002 / make / make.c < prev    next >
C/C++ Source or Header  |  1995-03-17  |  16KB  |  577 lines

  1. #include <stdio.h>
  2. #include <ctype.h>
  3. #include "make.h"
  4.  
  5. /*
  6.  *    MAKE - Maintain separate source files
  7.  *
  8.  *    SYNOPSIS
  9.  *        MK [-f file] [-a] [-n] [-d] [-i] [-k] [name] ...
  10.  *           f: use 'file' instead of default makefile
  11.  *           a: assume all modules are obsolete (recompile everything)
  12.  *           n: don't recompile, just list steps to recompile
  13.  *           d: debugging (print tree, file info)
  14.  *         i: ignore return statuses from execution
  15.  *         k: if errors occur, propagate error status up tree; continue.
  16.  *           name: module name to recompile
  17.  *
  18.  *    AUTHOR
  19.  *        Landon M. Dyer, Atari Inc.
  20.  *
  21.  *    INCREDIBLY HACKED OVER BY
  22.  *        Eric C. Brown University of Utah.
  23.  *      Fred Fish, UniSoft Systems Inc  (for Commodore AMIGA)
  24.  *
  25.  *    HACKS
  26.  *        Added object library support (dummy names that inherit dates)
  27.  *      Added source library support (real parents)
  28.  *      Added direct execution capability.
  29.  *      Removed script file.
  30.  *      Added support for my macro based debugging package (fnf)
  31.  *      Ran through "indent" to change formatting (fnf)
  32.  */
  33.  
  34. #define INIT    "~INIT"        /* initialization macro */
  35. #define DEINIT    "~DEINIT"    /* de-init macro */
  36. #define BEFORE    "~BEFORE"    /* the per-root 'startup' method */
  37. #define AFTER    "~AFTER"    /* the per-root 'wrapup' method */
  38.  
  39. char *mfiles[] = {        /* default makefiles */
  40.     "makefile",
  41.     "Makefile",
  42.  
  43. #ifdef VAXVMS
  44.     "[-]makefile",
  45.     "sys$login:makefile",
  46. #endif
  47.  
  48. #ifdef MSDOS
  49.     "..\makefile",
  50. #endif
  51.     ""
  52. };
  53.  
  54. MACRO *mroot = (MACRO *) NULL;        /* root of macro-list */
  55. FILENODE *froot = (FILENODE *) NULL;    /* root of filenode-list */
  56. FILENODE *firstf = (FILENODE *) NULL;    /* the very first filenode */
  57.  
  58. char *modnames[MAXMODS];    /* module-names mentioned in commandline */
  59. int execstat = 0;        /* nonzero if started executing */
  60. int modcount = 0;        /* #of module-names */
  61. int debug = 0;            /* nonzero: turn on debugging */
  62. int obsolete = 0;        /* nonzero: every file should be recompiled */
  63. int noscript = 0;        /* nonzero: print methods on stdout */
  64. int ignore_errors = 0;        /* nonzero: ignore error return codes */
  65. int prop_errors = 0;        /* nonzero: propagate error status up tree */
  66. DATE bigbang;            /* a date, the very earliest possible */
  67. DATE endoftime;            /* a date, the very last possible */
  68.  
  69. /*
  70.  *    The following are used to save macro definitions given on the
  71.  *    command line.  In the unix make, it is very common to override
  72.  *    a definition in the makefile via a definition on the command line.
  73.  *    However, this make expands macros in each line as they are read,
  74.  *    which conflicts with the need to wait until the entire file is read
  75.  *    before adding command line macros to the table (to override those
  76.  *    from the file).  Thus the code needs to be modified to delay macro
  77.  *    expansions until the last minute, before shipping the line off to
  78.  *    be executed. The alternative is to implement a macro locking or
  79.  *    precedence scheme.  Either solution requires more work than I can
  80.  *    do at the moment.  Fred Fish  28-Nov-85
  81.  */
  82. char *cmdmacros[MAXCMDMACS];    /* Macro defs given on command line */
  83. int cmdmcount = 0;        /* Number of macro defs on command line */
  84.  
  85. static void fparse ();
  86. static void yankdependents ();
  87. static void addmanydepend ();
  88. static void determ ();
  89. static void recomp ();
  90.  
  91. main (argc, argv)
  92. int argc;
  93. char *argv[];
  94. {
  95.     register int arg;
  96.     register int i;
  97.     register char *mfile = NULL;
  98.     extern DATE adate ();
  99.     extern void initrootdates ();
  100.     extern void prtree ();
  101.  
  102.     DBUG_ENTER ("main");
  103.     ENABLE_ABORT;
  104.     initrootdates ();
  105.     for (arg = 1; arg < argc; ++arg) {
  106.     if (*argv[arg] == '-') {
  107.         switch (tolower (argv[arg][1])) {
  108.         case '#':
  109.             DBUG_PUSH (&argv[arg][2]);
  110.             break;
  111.         case 'f': 
  112.             if (++arg >= argc) {
  113.             fputs ("-f needs filename argument.\n", stderr);
  114.             DBUG_RETURN (1);
  115.             }
  116.             mfile = argv[arg];
  117.             break;
  118.         case 'a': 
  119.             obsolete = 1;
  120.             break;
  121.         case 'n': 
  122.             noscript = 1;
  123.             break;
  124.         case 'd': 
  125.             debug = 1;
  126.             break;
  127.         case 'i': 
  128.             ignore_errors = 1;
  129.             break;
  130.         case 'k': 
  131.             prop_errors = 1;
  132.             break;
  133.         default: 
  134.             fputs ("Unknown switch: ", stderr);
  135.             fputc (argv[arg][1], stderr);
  136.             fputc ('\n', stderr);
  137.             break;
  138.         }
  139.     } else {
  140.         if (strchr (argv[arg], '=') != NULL) {
  141.         if (cmdmcount < MAXCMDMACS) {
  142.             cmdmacros[cmdmcount++] = argv[arg];
  143.         } else {
  144.             fputs ("Too many command line macros.\n", stderr);
  145.             DBUG_RETURN (1);
  146.         }
  147.         } else {
  148.         if (modcount < MAXMODS) {
  149.             modnames[modcount++] = argv[arg];
  150.         } else {
  151.             fputs ("Too many module names.\n", stderr);
  152.             DBUG_RETURN (1);
  153.         }
  154.         }
  155.     }
  156.     }
  157.     if (mfile != NULL) {
  158.     if (fmake (mfile) == -1) {
  159.         fputs ("Cannot open makefile '", stderr);
  160.         fputs (mfile, stderr);
  161.         fputs ("'.\n", stderr);
  162.     }
  163.     } else {
  164.     for (i = 0; *mfiles[i]; ++i) {
  165.         if (fmake (mfiles[i]) != -1) {
  166.         break;
  167.         }
  168.     }
  169.     if (!*mfiles[i]) {
  170.         fputs ("Cannot open makefile.\n", stderr);
  171.     }
  172.     }
  173.     if (debug) {
  174.     prtree ();
  175.     }
  176.     DBUG_RETURN (0);
  177. }
  178.  
  179.  
  180. /*
  181.  * Construct dependency tree from the makefile 'fn'.
  182.  * Figure out what has to be recompiled, and write a script file to do that.
  183.  */
  184.  
  185. fmake (fn)
  186. char *fn;
  187. {
  188.     FILE * fp;
  189.  
  190.     DBUG_ENTER ("fmake");
  191.     if ((fp = fopen (fn, "r")) == (FILE *) NULL) {
  192.     DBUG_RETURN (-1);
  193.     }
  194.     fparse (fp);
  195.     determ ();
  196.     fclose (fp);
  197.     DBUG_RETURN (0);
  198. }
  199.  
  200.  
  201. /*
  202.  * Parse the input file, defining macros and building the dependency tree.
  203.  */
  204.  
  205. static void fparse (fp)
  206. FILE *fp;
  207. {
  208.     auto char ibuf[STRSIZ];
  209.     auto char ebuf[STRSIZ];
  210.     auto char *strp;
  211.     register char *tok1;
  212.     register char *tok2;
  213.     register char *s;
  214.     extern char *fgets ();
  215.     register FILENODE *lastf = (FILENODE *)NULL;
  216.     extern FILENODE *addfile ();
  217.     extern void defmac ();
  218.     extern void AddToLibrary ();
  219.     extern void escape ();
  220.     extern void addmeth ();
  221.  
  222.     DBUG_ENTER ("fparse");
  223.     for (;;) {
  224.     if (fgets (ibuf, STRSIZ, fp) == NULL) {
  225.         break;
  226.     }
  227.     DBUG_3 ("inline", "got line '%s'", ibuf);
  228.     mexpand (ibuf, ebuf, STRSIZ, MACCHAR);
  229.     escape (ebuf, COMCHAR);
  230.     s = ebuf + strlen (ebuf) - 1;    /* clobber last newline in string */
  231.     if (s >= ebuf && *s == '\n') {
  232.         *s = '\0';
  233.     }
  234.     DBUG_3 ("inline2", "after macro and excape processing is '%s'", ebuf);
  235.     if (ebuf[0] == '\t' || ebuf[0] == ' ') {
  236.         DBUG_2 ("meth", "looks like a method line to me");
  237.         addmeth (lastf, ebuf);
  238.         continue;
  239.     }
  240.     strp = ebuf;
  241.     if ((tok1 = token (&strp)) == NULL) {
  242.         continue;
  243.     }
  244.     if ((tok2 = token (&strp)) != NULL) {
  245.         if (STRSAME (tok2, DEFMAC)) {
  246.         DBUG_2 ("mac", "looks like a macro definition to me");
  247.         if (*strp) {
  248.             defmac (tok1, strp);
  249.         }
  250. #if VAXVMS || MSDOS
  251.         /* Preserve old behavior.  Unix and amiga behavior is */
  252.         /* to never undefine any macros, in the sense used here. */
  253.         /* This also allows macros with null expansions, which */
  254.         /* are very useful.  Fred Fish */
  255.         if (!*strp) {
  256.             if (undefmac (tok1) < 0) {
  257.             fputs ("Can't undefine macro '", stderr);
  258.             fputs (tok1, stderr);
  259.             fputs ("'.\n", stderr);
  260.             }
  261.         }
  262. #endif
  263.         continue;
  264.         } else if (STRSAME (tok2, DEPEND)) {
  265.         DBUG_2 ("mac", "looks like a dependency line to me");
  266.         addmeth (lastf, gmacro (AFTER)); /* terminate last method */
  267.         lastf = filenode (tok1);    /* init lastf */
  268.         if (firstf == (FILENODE *) NULL) {
  269.             firstf = lastf;
  270.         }
  271.         lastf -> fmake = NULL;
  272.         addmeth (lastf, gmacro (BEFORE));
  273.         lastf -> fflag |= ROOTP;
  274.         addmanydepend (strp, lastf);
  275.         continue;
  276. #ifndef FUNNYLIBS
  277.         } else if (STRSAME (tok2, ISLIB)) {
  278.         addmeth (lastf, gmacro (AFTER));
  279.         lastf = filenode (tok1);
  280.         if (firstf == (FILENODE *) NULL) {
  281.             firstf = lastf;
  282.         }
  283.         lastf -> fmake = NULL;
  284.         addmeth (lastf, gmacro (BEFORE));
  285.         lastf -> fflag |= LIBRARY;
  286.         lastf -> fflag |= ROOTP;
  287.  
  288.         AddToLibrary (lastf);
  289.         /* no archives here */
  290.         /* archives and libraries are mutually exclusive */
  291.         while ((tok1 = token (&strp)) != NULL) {
  292.             (void) addfile (lastf, tok1);
  293.         }
  294.         continue;
  295. #endif
  296.         } else {
  297.         DBUG_2 ("uh", "what kinda line is this?");
  298.         addmanydepend (strp, lastf);
  299.         }
  300.     }
  301.     }
  302.     addmeth (lastf, gmacro (AFTER));
  303.     DBUG_VOID_RETURN;
  304. }
  305.  
  306. /*
  307.  * scan tokens from strbuf and search for libraries and archives.
  308.  * libraries look like foo [ bar baz mumble ]
  309.  * archives look like foo ( bar baz mumble )
  310.  * in either case, bar, baz, and mumble have parents of foo.
  311.  * foo is added to the parentlist, if not already on the list.
  312.  * bar, baz, and mumble are added to the dependency list of depend.
  313.  * the command *cannot* be split across newlines without causing errors.
  314.  * if you don't like that, well, life's a bitch and then you die.
  315.  */
  316.  
  317. static void addmanydepend (strbuf, depend)
  318. char *strbuf;
  319. FILENODE *depend;
  320. {
  321.     register char *tok1;
  322.     register char *tok2;
  323.     register FILENODE *parent;
  324.     register FILENODE *child;
  325.     extern FILENODE *addfile ();
  326.     extern FILENODE *addparent ();
  327.     extern void exit ();
  328.  
  329.     DBUG_ENTER ("addmanydepend");
  330.     DBUG_4 ("dep", "add dependencies '%s' to '%s'", strbuf, depend -> fname);
  331.     tok1 = token (&strbuf);
  332.     if (tok1 == NULL) {
  333.     DBUG_VOID_RETURN;
  334.     }
  335.     tok2 = token (&strbuf);
  336.     while (tok2 != NULL) {
  337. #ifdef FUNNYLIBS
  338.     if (STRSAME (tok2, BGNLIB)) {
  339.         parent = addparent (tok1);    /* add tok1 to parent list */
  340.         for (tok1 = token (&strbuf);    /* skip over token in tok2 */
  341.          tok1 != NULL && strcmp (tok1, ENDLIB);    /* go eol or end */
  342.          tok1 = token (&strbuf)) {    /* get next token */
  343.         if (tok1 == NULL) {
  344.             fputs ("MAKE: Error in library defn.\n", stderr);
  345.             exit (2);
  346.         }
  347.         child = addfile (depend, tok1);
  348.         child -> fflag = LIBRARY;
  349.         child -> parent = parent;
  350.         }            /* for */
  351.         tok1 = token (&strbuf);
  352.         tok2 = token (&strbuf);
  353.         continue;        /* the while */
  354.     }            /* if islib */
  355. #endif
  356.     if (STRSAME (tok2, BGNARC)) {
  357.         parent = addparent (tok1);        /* add tok1 to parent list */
  358.         for (tok1 = token (&strbuf);    /* skip over token in tok2 */
  359.          tok1 != NULL && strcmp (tok1, ENDARC);    /* go eol or end */
  360.          tok1 = token (&strbuf)) {    /* get next token */
  361.         if (tok1 == NULL) {
  362.             fputs ("MAKE: Error in archive defn.\n", stderr);
  363.             exit (2);
  364.         }
  365.         child = addfile (depend, tok1);
  366.         child -> fflag = ARCHIVE;
  367.         child -> parent = parent;
  368.         }            /* for */
  369.         tok1 = token (&strbuf);/* get current token */
  370.         tok2 = token (&strbuf);/* get lookahead token */
  371.         continue;        /* the while */
  372.     }            /* if isarc */
  373.     else {            /* nothing special -- */
  374.         (void) addfile (depend, tok1);/* add dependency */
  375.         tok1 = tok2;    /* shift token */
  376.         tok2 = token (&strbuf);
  377.     }
  378.     }                /* while */
  379.     if (tok2 == NULL && tok1 != NULL) {        /* last token = not special */
  380.     (void) addfile (depend, tok1);
  381.     }
  382.     DBUG_VOID_RETURN;
  383. }
  384.  
  385. /*
  386.  * Determine sequence of recompiles from the creation dates.
  387.  * If have anything to recompile, then create a script file full of commands.
  388.  */
  389.  
  390. static void determ ()
  391. {
  392.     register FILENODE *f;
  393.     register int i;
  394.     register char *m;
  395.     extern void cleanuparchives ();
  396.  
  397.     DBUG_ENTER ("determ");
  398.     if (firstf == (FILENODE *) NULL) {        /* empty tree */
  399.     puts ("No changes.");
  400.     DBUG_VOID_RETURN;
  401.     }
  402.     if (modcount == 0) {
  403.     examine (firstf, endoftime);
  404.     } else {
  405.     for (i = 0; i < modcount; ++i) {
  406.         if ((f = gfile (modnames[i])) == (FILENODE *) NULL) {
  407.         fputs ("Don't know how to make ", stderr);
  408.         fputs (modnames[i], stderr);
  409.         fputs (".\n", stderr);
  410.         continue;
  411.         }
  412.         if ((f -> fflag & ROOTP) == 0) {
  413.         fputc ('\'', stderr);
  414.         fputs (f -> fname, stderr);
  415.         fputs ("' is not a root!\n", stderr);
  416.         continue;
  417.         }
  418.         examine (f, endoftime);
  419.     }
  420.     }
  421.     if (execstat) {
  422.     if ((m = gmacro (DEINIT)) != NULL) {
  423.         execute (m, noscript);
  424.     }
  425.     cleanuparchives ();
  426.     } else {
  427.     puts ("No changes.");
  428.     }
  429.     DBUG_VOID_RETURN;
  430. }
  431.  
  432.  
  433. /*
  434.  * Examine filenode 'fnd' and see if it has to be recompiled.
  435.  * 'date' is the last-touched date of the node's father
  436.  * (or 'endoftime' if its a root file.)
  437.  * Root files with NO dependencies are assumed not to be up to date.
  438.  */
  439.  
  440. examine (fnd, date)
  441. FILENODE *fnd;
  442. DATE date;
  443. {
  444.     register int rebuildp = 0;
  445.     register int rval;
  446.     register int errcode = 0;
  447.     register NODE *n;
  448.     extern void getdate ();
  449.     extern char *printdate ();
  450.  
  451.     DBUG_ENTER ("examine");
  452.     DBUG_3 ("ex", "parent node date '%s'", printdate (date));
  453.     DBUG_3 ("ex", "examine node '%s'", fnd -> fname);
  454.     getdate (fnd);
  455.     DBUG_3 ("ex", "modification date '%s'", printdate (fnd -> fdate));
  456.     DBUG_3 ("ex", "parent node date '%s'", printdate (date));
  457.     if (fnd -> fnode == (NODE *) NULL && fnd -> fflag & ROOTP) {
  458.     DBUG_2 ("root", "node, is rootnode with no dependents, rebuild");
  459.     rebuildp = 1;
  460.     } else {            /* see if dependents need to be recompiled */
  461.     for (n = fnd -> fnode; n != (NODE *) NULL; n = n -> nnext) {
  462.         if ((rval = examine (n -> nfile, fnd -> fdate)) != 0) {
  463.         if (rval == ERROR) {
  464.             errcode = ERROR;/* if error occurred, propagate up */
  465.             fnd -> fflag |= ERROR;
  466.             fputs ("Couldn't remake ", stderr);
  467.             fputs (fnd -> fname, stderr);
  468.             fputs (" because of errors.\n", stderr);
  469.         }
  470.         rebuildp = 1;
  471.         }
  472.     }
  473.     }
  474.     DBUG_3 ("ex", "parent node date '%s'", printdate (date));
  475.     DBUG_3 ("rebuildp", "rebuild flag is %d", rebuildp);
  476.     /* if ancestor recompiled or root, recompile, */
  477.     /* but not if error in ancestor */
  478.     if (rebuildp && (fnd -> fflag & ERROR) == 0) {
  479.     DBUG_3 ("rebuild", "'%s' needs remaking", fnd -> fname);
  480.     recomp (fnd);
  481.     if (fnd -> fflag & ERROR) {
  482.         DBUG_3 ("err", "got an error remaking %s", fnd -> fname);
  483.         DBUG_RETURN (ERROR);
  484.     }
  485.     }
  486.     DBUG_3 ("ex", "current node date now '%s'", printdate (fnd -> fdate));
  487.     DBUG_3 ("ex", "parent node date '%s'", printdate (date));
  488.     if (obsolete || laterdt (fnd -> fdate, date) >= 0) {
  489.     DBUG_2 ("date", "looks like parent needs remaking now");
  490.     rebuildp = 1;
  491.     }
  492.     if (errcode) {
  493.     DBUG_RETURN (errcode);
  494.     } else {
  495.     DBUG_RETURN (rebuildp);
  496.     }
  497. }
  498.  
  499. /*
  500.  * Make sure a filenode gets recompiled.
  501.  */
  502.  
  503. static void recomp (f)
  504. FILENODE *f;
  505. {
  506.     register char *m;
  507.  
  508.     DBUG_ENTER ("recomp");
  509.     if (!execstat) {
  510.     execstat = 1;
  511.     if ((m = gmacro (INIT)) != NULL) {
  512.         execute (m, noscript);
  513.     }
  514.     }
  515.     if (f -> fflag & REBUILT) {
  516.     DBUG_VOID_RETURN;
  517.     }
  518.     if (!noscript) {        /* don't extract if printing steps */
  519.     yankdependents (f);
  520.     }
  521.     if (f -> fmake != NULL) {
  522.     if (execute (f -> fmake, noscript) != 0) {
  523.         if (!ignore_errors && !prop_errors) {
  524.         exit (2);
  525.         } else if (prop_errors) {
  526.         f -> fflag |= ERROR;
  527.         }
  528.     }
  529.     }
  530.     f -> fflag |= REBUILT;
  531.     DBUG_VOID_RETURN;
  532. }
  533.  
  534. static void yankdependents (fnd)
  535. FILENODE *fnd;
  536. {
  537.     register NODE *n;
  538.     extern int extract ();
  539.  
  540.     DBUG_ENTER ("yankdependents");
  541.     for (n = fnd -> fnode; n != (NODE *) NULL; n = n -> nnext) {
  542. #ifdef YANKDESCENDANTS
  543.     yankdependents (n -> nfile);
  544. #endif
  545.     DBUG_3 ("dep", "yanking %s", n -> nfile -> fname);
  546.     DBUG_3 ("dep", "flags %d", n -> nfile -> fflag);
  547.     if ((n -> nfile -> fflag & ARCHIVE) && ((n -> nfile -> fflag & EXTRACT) == 0)) {
  548.         /* if archived and not extracted */
  549.         fputs ("Extracting ", stdout);
  550.         puts (n -> nfile -> fname);
  551.         if (!noscript) {
  552. #ifdef LAR
  553.         if (extract (n -> nfile) == FAILURE) {
  554.             fputs ("Extract failed -- I think I'll die now.\n", stderr);
  555.             exit (1);
  556.         }
  557. #else
  558.         fputs ("No support for archives, bye!\n", stderr);
  559.         exit (1);
  560. #endif
  561.         }
  562.         n -> nfile -> fflag |= EXTRACT;
  563.     }
  564.     }
  565.     DBUG_VOID_RETURN;
  566. }
  567.  
  568. /*
  569.  * Complain about being out of memory, and then die.
  570.  */
  571.  
  572. allerr ()
  573. {
  574.     fputs ("Can't alloc -- no space left (I give up!)\n", stderr);
  575.     exit (1);
  576. }
  577.